home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 3
/
Gold Medal Software - Volume 3 (Gold Medal) (1994).iso
/
utils1
/
iochek.arj
/
IOCHEK.PAS
< prev
Wrap
Pascal/Delphi Source File
|
1993-12-13
|
69KB
|
1,736 lines
unit IOChek;
{ Posted on CompuServe by Mark Reichert, 72763,2417 }
{ This is a unit containing many functions for handling I/O operations and
returning the error code in the function return, though any that encapsulate
BP functions will return that information in the function return and the error
code in a VAR parameter, i.e. the EOF function here is still Boolean and
returns the error as a parameter. As I explain below, I made this unit
because when I was in a hurry, I improperly handled error conditions with the
GetFAttr procedure. All the work with Enums and Strings is to ease detecting
what error occured and generating a message. I even use the Enums in some
of the functions below in case statements as they are intended to be used.
This unit is provide AS IS. I DO NOT guarantee that there are no defects,
though I have been over the code several times. I am providing this for
free. I only ask that anybody that does find a defect please send me an
e-mail pointing it out so that I can slowly refine the unit. It is really
my hope that with this as an example, one of the Pascal third party support
houses or even Borland itself will provide a unit that eases I/O operations
for people who cannot do to previous work use the event driven, windowed
interfaces of Turbo Vision or ObjectWindows. I do not have all of the
obscure Microsoft documentation or the experience that would allow me to
interpret the error codes correctly and do refined, robust effort. One
programmer pointed out, in a message reply to my previous plea for a good
I/O unit, that in network environment, a file read could return a file lock
error. In that case, I would think you could write a procedure that would
go into a loop to keep checking until the file is free or a parameter set
timeout limit is reached. And, yes, I know all the encapsulation adds
overhead, but its insignificant compared to the file I/O itself. Besides,
as machines get faster, more cycles can be used to make the programs more
reliable, which will be necessary as people with progressively less
experience with computers start using them.
Any way, I'd like to thank Neil Rubenking for his permission for using the
DosFlush procedure and Jeffrey Watson, a former , who wrote the
original code on which a number of the functions are based.
Thanks in advance for any replies.
Mark Reichert, 72763,2417}
Interface
Uses Dos, Objects ; { Objects used for the Abstract function }
Type Strg12 = String[12];
Strg40 = String[40];
ByteSeg = array [1..65535] of byte ;
PByteSeg = ^ByteSeg ;
FileTypeEnum = (TextFile, Typed, UnTyped);
OpenTextEnum = (ResetFile, RewriteFile, AppendFile);
OpenFileEnum = ResetFile .. RewriteFile;
{ Error codes only go to 181 due to something I observed in MAIN.ASM in the
Run-Time Library SYS directory. The SYS directory contains all the code
that ends up in the SYSTEM special unit that sits in the TURBO.TPL file,
is loaded into memory when loading the IDE or command-line compiler, and
forms the core code of every Pascal program. Main contains the very first
code your programs execute, including the loading of special interrupt
functions over any previously installed by DOS or a TSR. One of those,
you would see is the Int 24 Critical Error Handler which as the QUE DOS
Programmers Reference, 3rd edition defines as "the routine that receives
control when a critical error is detected. A critical error generally
represents a hardware failure of some sort and is usually the aftermath
of a failed device driver call within DOS." That same reference shows that
the error code returned in the DI register for this routine correspond to
those identified as DOS 3.0 errors below, that is 150 and above. The first
thing BP's Int 24 does is AND DI, 01FH which means only the lower 5 bits
are retained and then ADD DI, 150, so as even the comment in MAIN.ASM
declares the error code in DI has been translated to the range 150..181.
However, there is a better way. The Int 21, Function 59H (Get Extended
Error Information), which was first available in DOS 3.0, is safe to call
from an Int 24 handler, as is Function 30H (Get Dos Version). So, as is
done in ParamStr to allow ParamStr(0) = Program Path and FileName, the
Int 24 handler could check the DosVersion and if Dos 3+ is running, make
the call to 59H and place the return values in special system variables,
otherwise zero those variables out and do things the old way. It would
also be necessary to have I/O checking code do this. This way more
information about the error would be available to programs running on top
of Dos 3 or greater. After all, any program that depends on getting a
non-null string from ParamStr(0) in order to force itself to be run
from its directory, as one of mine does, has to be run on Dos 3 or better.
I'm also using Function 5A (Create Uniquely Named File) for a swap file,
and that function needs Dos 3+ as well. }
ErrorEnum = ( NoError, { 0 }
InvalidFunc, { 1 }
FileNotFound, { 2 }
PathNotFound, { 3 }
NoHandlesAvail, { 4 }
AccessDenied, { 5 }
InvalidHandle, { 6 }
MCBDestroyed, { 7 }
InsufficientMemory, { 8 }
InvalidMemBlock, { 9 }
InvalidEnviron, { 10 }
InvalidFormat, { 11 }
InvalidAccess, { 12 }
InvalidData, { 13 }
Reserved0, { 14 }
InvalidDrive, { 15 }
AttemptRemCurrDir, { 16 }
NotSameDevice, { 17 }
NoMoreFiles, { 18 }
Dummy19,
Dummy20, Dummy21, Dummy22, Dummy23, Dummy24, Dummy25, Dummy26, Dummy27, Dummy28, Dummy29,
Dummy30, Dummy31, Dummy32, Dummy33, Dummy34, Dummy35, Dummy36, Dummy37, Dummy38, Dummy39,
Dummy40, Dummy41, Dummy42, Dummy43, Dummy44, Dummy45, Dummy46, Dummy47, Dummy48, Dummy49,
Dummy50, Dummy51, Dummy52, Dummy53, Dummy54, Dummy55, Dummy56, Dummy57, Dummy58, Dummy59,
Dummy60, Dummy61, Dummy62, Dummy63, Dummy64, Dummy65, Dummy66, Dummy67, Dummy68, Dummy69,
Dummy70, Dummy71, Dummy72, Dummy73, Dummy74, Dummy75, Dummy76, Dummy77, Dummy78, Dummy79,
Dummy80, Dummy81, Dummy82, Dummy83, Dummy84, Dummy85, Dummy86, Dummy87, Dummy88, Dummy89,
Dummy90, Dummy91, Dummy92, Dummy93, Dummy94, Dummy95, Dummy96, Dummy97, Dummy98, Dummy99,
DiskReadError, { 100 }
DiskWriteError, { 101 }
FileNotAssigned, { 102 }
FileNotOpen, { 103 }
FileNotOpenForInput, { 104 }
FileNotOpenForOutput, { 105 }
InvalidNumericFormat, { 106 }
Dummy107, Dummy108, Dummy109,
Dummy110, Dummy111, Dummy112, Dummy113, Dummy114, Dummy115, Dummy116, Dummy117, Dummy118, Dummy119,
Dummy120, Dummy121, Dummy122, Dummy123, Dummy124, Dummy125, Dummy126, Dummy127, Dummy128, Dummy129,
Dummy130, Dummy131, Dummy132, Dummy133, Dummy134, Dummy135, Dummy136, Dummy137, Dummy138, Dummy139,
Dummy140, Dummy141, Dummy142, Dummy143, Dummy144, Dummy145, Dummy146, Dummy147, Dummy148, Dummy149,
DiskWriteProtect, { 150 }
UnknownUnit, { 151 }
DriveNotReady, { 152 }
UnknownCommand, { 153 }
CRCErrorinData, { 154 }
BadReqStructLeng, { 155 }
DiskSeekError, { 156 }
UnknownMediaType, { 157 }
SectorNotFound, { 158 }
OutOfPaper, { 159 }
DeviceWriteFault, { 160 }
DeviceReadFault, { 161 }
GeneralFailure, { 162 }
SharingViolation, { 163 }
LockViolation, { 164 }
InvalidDiskChange, { 165 }
FCBUnavailable, { 166 }
SharingBufferOverflow,{ 167 }
CodePageMismatch, { 168 }
EndofInputFile, { 169 }
DiskFull, { 170 }
Reserved1, { 171 }
Reserved2, { 172 }
Reserved3, { 173 }
Reserved4, { 174 }
Reserved5, { 175 }
Reserved6, { 176 }
Reserved7, { 177 }
Reserved8, { 178 }
Reserved9, { 179 }
Reserved10, { 180 }
UnsupportedNetworkReq, { 181 }
UnknownDOSError) ;
Const
ErrorStr : Array[ErrorEnum] of Strg40 =
{ Regular I/O Errors }
( 'No Error', { 0 }
'Invalid Function', { 1 }
'File Not Found', { 2 }
'Path Not Found', { 3 }
'No Handles Available', { 4 }
'Access Denied', { 5 }
'Invalid Handle', { 6 }
'Memory Control Block Destroyed',{ 7 }
'Insufficent Memory', { 8 }
'Invalid Memory Block', { 9 }
'Invalid Environment', { 10 }
'Invalid Format', { 11 }
'Invalid Access', { 12 }
'Invalid Data', { 13 }
'Reserved', { 14 }
'Invalid Drive', { 15 }
'Attempt To Remove Current Directory',{ 16 }
'Attempt To Rename Across Drives', { 17 }
'No More Files', { 18 }
'',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
{ Borland Pascal Determined Errors }
'Disk Read Error', { 100 }
'Disk Write Error', { 101 }
'File Not Assigned', { 102 }
'File Not Open', { 103 }
'File Not Open For Input', { 104 }
'File Not Open For Output',{ 105 }
'Invalid Numeric Format', { 106 }
'', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
{ Critical Errors }
'Disk Write Protected', { 150 }
'Unknown Unit', { 151 }
'Drive Not Ready', { 152 }
'Unknown Command', { 153 }
'CRC Error in Data', { 154 }
'Bad Requested Structure Length', { 155 }
'Disk Seek Error', { 156 }
'Unknown Media Type', { 157 }
'Sector Not Found', { 158 }
'Out Of Paper', { 159 }
'Device Write Fault', { 160 }
'Device Read Fault', { 161 }
'General Failure', { 162 }
'Sharing Violation', { 163 }
'Lock Violation', { 164 }
'Invalid Disk Change', { 165 }
'File Control Block Unavailable', { 166 }
'Sharing Buffer Overflow', { 167 }
'Code Page Mismatch', { 168 }
'End of Input File', { 169 }
'Disk Full', { 170 }
'Reserved', { 171 }
'Reserved', { 172 }
'Reserved', { 173 }
'Reserved', { 174 }
'Reserved', { 175 }
'Reserved', { 176 }
'Reserved', { 177 }
'Reserved', { 178 }
'Reserved', { 179 }
'Reserved', { 180 }
'Unsupported Network Request', { 181 }
'Unknown DOS Error') ;
Type
PFindFileObj = ^TFindFileObj;
TFindFileObj = Object
FFError : Integer;
FSearch : Dos.SearchRec;
FAttr : Byte;
FTime : Longint;
FYear : Word;
FMonth : Word;
FDay : Word;
FHour : Word;
FMin : Word;
FSec : Word;
FSize : Longint;
FNameExt : String[12];
FName : String[8];
FExt : String[3];
Constructor InitAndFindFirst(Const Path : PathStr; Attr: Word);
Destructor EndFindFile;
Procedure DoFindNext;
Procedure ParseFSearch;
Function DoFindFileLoop : Integer;
Function DoFileOperation : Integer; Virtual;
End;
PCopyFileObj = ^TCopyFileObj;
TCopyFileObj = Object
Err : Integer;
BuffSize : Word;
CopyBuffer : PByteSeg;
SourceFile : File;
DestFile : File;
SourceFullPath : PathStr;
SourcePath : PathStr;
SourceName : Strg12;
DestFullPath : PathStr;
DestPath : PathStr;
DestName : Strg12;
Constructor InitCopy(Const Source : PathStr; Const Dest : PathStr);
Destructor EndCopy; Virtual;
Procedure SetNames(Const FileName : Strg12);
Procedure SetPaths(Const SPath : PathStr; Const DPath : PathStr);
Function GetErr : Integer;
Function OpenFiles : Integer;
Function CloseFiles : Integer;
Function DoCopies : Integer;
Function DoFileCopy : Integer; Virtual;
End;
PMoveFileObj = ^TMoveFileObj;
TMoveFileObj = Object(TCopyFileObj)
Constructor InitMove(Source, Dest : PathStr);
Destructor EndMove; Virtual;
Function DoFileCopy : Integer; Virtual;
End;
Function IO_IsCoveredError(ErrorNo : Integer) : Boolean;
Function IO_GetErrorEnum(ErrorNo : Integer) : ErrorEnum;
Function IO_GetErrorStr(ErrorNo : Integer) : Strg40;
Function IO_GetErrorStrFromEnum(Error : ErrorEnum) : Strg40;
Function IO_AddSlash(InPath : String) : String;
Function IO_DelSlash(InPath : String) : String;
Function IO_EditPathForDos(InPath : String) : String;
Function IO_DosFlush(Var F) : Word;
Function IO_ChDir(CONST Path : PathStr) : Integer;
Function IO_CD(CONST Path : PathStr) : Integer;
Function IO_GD(Drive : char) : PathStr ;
Function IO_MkDir(Const Path : PathStr) : Integer ;
Function IO_MD(Const Path : PathStr) : Integer ;
Function IO_RmDir(Const Path : PathStr) : Integer ;
Function IO_RD(Const Path : PathStr) : Integer;
Function IO_ShareInstalled : Boolean ;
Function IO_ReadOnly : Byte ;
Function IO_ReadWrite : Byte ;
Function IO_WriteOnly : Byte ;
Function IO_FileMode(InMode : Word) : Byte ;
Function IO_ResetText(Var TextFile : Text) : Integer;
Function IO_RewriteText(Var TextFile : Text) : Integer;
Function IO_AppendText(Var TextFile : Text) : Integer;
Function IO_OpenText(Const PathName : PathStr;
Var TextFile : Text;
OpenType : OpenTextEnum) : Integer;
Function IO_ReadTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
Function IO_ReadLnTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
Function IO_WriteTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
Function IO_WriteLnTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
Function IO_ResetFile(Var GenericFile : File) : Integer;
Function IO_RewriteFile(Var GenericFile : File) : Integer;
Function IO_OpenFile( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum) : Integer;
Function IO_ResetFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
Function IO_RewriteFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
Function IO_OpenFileBlock( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum;
BufSize : Word) : Integer;
Function IO_ResetFileBlock1(Var GenericFile : File) : Integer;
Function IO_RewriteFileBlock1(Var GenericFile : File) : Integer;
Function IO_OpenFileBlock1( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum) : Integer;
Function IO_BlockRead( Var GenericFile : File;
Var Buffer;
Count : Word;
Var BytesRead : Word ) : Integer;
Function IO_BlockReadIntoHeap( Const PathName : PathStr;
Var BuffPtr : Pointer;
Var FSize : Longint ) : Integer;
Function IO_BlockWrite( Var GenericFile : File;
Var Buffer;
Count : Word;
Var Result : Word) : Integer;
Function IO_BlockWriteFromHeap(Const PathName : PathStr;
BuffPtr : Pointer;
FSize : Word ) : Integer;
Function IO_Close(Var GenericFile : File) : Integer;
Function IO_CloseText(Var TextFile : Text) : Integer;
Function IO_CloseFile(Var GenericFile : File) : Integer;
Function IO_CloseTextFile(Var TextFile : Text) : Integer;
Function IO_FlushToDos(Var TextFile : Text) : Integer;
Function IO_FlushToDisk(Var TextFile : Text) : Integer;
Function IO_FilePos(Var GenericFile : File; Var FPos : Longint) : Integer;
Function IO_FileSeek(Var GenericFile : File; FSeek : Longint) : Integer;
Function IO_GoFileSeek( Const PathName : PathStr;
Var GenericFile : File;
FSeek : Longint) : integer ;
Function IO_EOF(Var GenericFile : File; Var ErrCode : Integer) : Boolean;
Function IO_EOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Function IO_SeekEOF(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Function IO_SeekEOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Function IO_GetFTime(Var GenericFile : File; Var InTime : Longint) : Integer;
Function IO_SetFTime(Var GenericFile : File; InTime : Longint) : Integer;
Function IO_GetFileTime(Const PathName : PathStr; Var Time : Longint) : Integer;
Function IO_SetFileTime(Const PathName : PathStr ; Time : Longint) : Integer;
Function IO_GetFAttr(Var GenericFile : File; Var Attr : word) : Integer;
Function IO_GetFileAttr(Const PathName : PathStr; Var Attr : word) : Integer;
Function IO_SetFAttr(Var GenericFile : File; Attr : word) : Integer ;
Function IO_SetFileAttr(Const PathName : PathStr; Attr : word) : Integer;
Function IO_FileIsHere(FullName : PathStr) : Boolean;
Function IO_Exists(Path : PathStr ; Attr : byte) : Boolean ;
Function IO_RenameFile( Const OldName : PathStr;
Const NewName : PathStr) : integer ;
Function IO_Erase(Var GenericFile : File) : Integer;
Function IO_EraseText(Var TextFile : Text) : Integer;
Function IO_EraseFile(Const PathName : PathStr) : integer ;
Function IO_KillAFile(Const PathName : PathStr) : integer ;
Function IO_EraseFiles(Path,FileSpec : PathStr) : integer ;
Function IO_Remove(InPath : PathStr) : Integer;
Function IO_FileSize(Var GenericFile : File; Var Size : Longint) : Integer;
Function IO_GetNoOfRecords(Const PathName : PathStr; Var Size : Longint; RecordSize : Word) : integer ;
Function IO_GetFileSize(Const PathName : PathStr; Var FileSize : Longint) : integer ;
Function IO_Equals(Var X, Y; Index : word; Size : word) : Boolean;
Type
IOErrorValues = set of 0..181; { Set of valid error codes }
Const
RegIOErrors : IOErrorValues = [ord(NoError)..Ord(NoMoreFiles)];
BPRTLErrors : IOErrorValues = [ord(DiskReadError)..ord(InvalidNumericFormat)];
CritErrors : IOErrorValues = [ord(DiskWriteProtect)..ord(UnsupportedNetworkReq)];
CoveredErrorNumbers : IOErrorValues = [ ord(NoError)..ord(NoMoreFiles),
ord(DiskReadError)..ord(InvalidNumericFormat),
ord(DiskWriteProtect)..ord(UnsupportedNetworkReq)];
CopyPtr : PCopyFileObj = Nil;
MovePtr : PMoveFileObj = Nil;
Implementation
Function IO_IsCoveredError(ErrorNo : Integer) : Boolean;
Begin
IO_IsCoveredError := ErrorNo in CoveredErrorNumbers;
End;
Function IO_GetErrorEnum(ErrorNo : Integer) : ErrorEnum;
Begin
If IO_IsCoveredError(ErrorNo) Then
IO_GetErrorEnum := ErrorEnum(ErrorNo)
Else
IO_GetErrorEnum := UnknownDOSError;
End;
Function IO_GetErrorStr(ErrorNo : Integer) : Strg40;
Begin
If IO_IsCoveredError(ErrorNo) Then
IO_GetErrorStr := ErrorStr[ErrorEnum(ErrorNo)]
Else
IO_GetErrorStr := ErrorStr[UnknownDOSError];
End;
Function IO_GetErrorStrFromEnum(Error : ErrorEnum) : Strg40;
Begin
IO_GetErrorStrFromEnum := ErrorStr[Error];
End;
{Dos Conversion }
{ Adds BackSlash at end of Path if not one there already }
Function IO_AddSlash(InPath : String) : String; assembler;
asm
mov dx, ds
les di, @Result { get result destination }
lds si, InPath { get address of Instr }
xor ax, ax
lodsb { load InPath length byte }
stosb { store it in Result length byte }
mov cx, ax
jcxz @1 { skip rest if an empty string }
rep movsb { move Instr to Result }
cmp byte ptr es:[di-1], '\' { see if there is a slash at end }
je @1 { if there is, skip next step }
mov byte ptr es:[di], '\' { put '\' at end }
les di, @Result { get result destination }
inc byte ptr es:[di] { increase length byte }
@1:
mov ds, dx
end;
{Dos Conversion }
{ Deletes BackSlash at end of Path if one is there }
Function IO_DelSlash(InPath : String) : String; assembler;
asm
mov dx, ds
les di, @Result { get result destination }
lds si, InPath { get address of Instr }
xor ax, ax
lodsb { load InPath length byte }
mov bx, ax { put length in bx for indexing }
cmp byte ptr [bx+si-1], '\' { see if there is a slash at end }
jne @1 { if there isn't, skip next step }
dec ax { decrease length }
@1:
stosb { store it in Result length byte }
mov cx, ax
rep movsb { move Instr to Result }
mov ds, dx
end;
{Dos Conversion }
{ This Function replaces a Pascal if statement, which edited the path
before giving it to the BP procedure, that was in several procedures }
{ Deletes BackSlash at end of Path if one is there, except when a
a backslash is valid like '\' or 'A:\' for the root directory }
Function IO_EditPathForDos(InPath : String) : String; assembler;
asm
mov dx, ds
les di, @Result { get result destination }
lds si, InPath { get address of Instr }
xor ax, ax
lodsb { load InPath length byte }
cmp al, 1
je @1 { if Instr is 1 char, need not change }
mov bx, ax { put length in bx for indexing }
cmp byte ptr [si+bx-1], '\' { see if there is a slash at the end of the string }
jne @1 { if there isn't, skip next step }
cmp byte ptr [si+bx-2], ':' { see if it was a valid use like 'A:\'}
je @1 { if it was, skip next step }
dec ax { decrease length }
@1:
stosb { store it in Result length byte }
mov cx, ax
rep movsb { move Instr to Result }
mov ds, dx
end;
{ This Function is from Turbo Pascal 6.0 Techniques and Utilities,
pages 610-11, Copyright @ 1991 Neil Rubenking, who gave his permission
to use this Function. However, I added a conditional jump that was
left out, and moved two lines up in the Function so that they didn't
have to be duplicated in two areas. There are two points I should make
in addition to those in the book: 1) This is a word Function because it
returns the DOS Error code in the AX register Function return, almost
automatically and 2) This Function only flushes from the DOS buffers to
disk. To get BP buffers to the disk, you must first use the BP Flush,
and then call this Function as I do below in IO_FlushToDisk }
Function IO_DosFlush(Var F) : Word; assembler;
asm
mov ax, 3000h { get Dos Version }
int 21h
{ next two lines were duplicated in both flow control channels but
putting them here upstream of the CMPs accomplishes the same purpose }
les di, F { get address of file variable }
mov bx, ES:[DI] { File handle is first word }
cmp al, 3 { Dos < 3? old! }
jl @old
cmp ah, 1Eh { Dos < 3.3? old! }
jl @old { this line is not in published version, but is needed }
mov ah, 68h { commit file Function }
int 21h
jc @BadEnd { Carry Flag set on error, AX = Error Code, so leave }
jmp @GoodEnd { Finished! Function 68h handles all, unlike old below }
@old:
mov ah, 45h { duplicate handle Function }
int 21h
jc @BadEnd
@ok: { this label just 'names' the following code }
mov bx, ax { put duped handle in BX }
mov ah, 3Eh { and close it }
int 21h
jc @BadEnd
@GoodEnd:
mov ax, 0 { no error, so set return value to 0 }
@BadEnd:
end;
{ Change the directory to a presumably correct format Path and get any error }
Function IO_ChDir(CONST Path : PathStr) : Integer;
Begin
{$I-} chdir( Path ) ;
{$I+} IO_ChDir := IOResult ;
End;
{ Jeffrey Watson's original Pascal Edit path If statement has been
replaced with EditPatForDos, and the call to BP Chdir with IO_ChDir }
{ Don't presume that Path is correctly formatted, edit it,
then change the directory, and get any error }
Function IO_CD(CONST Path : PathStr) : Integer;
begin
IO_CD := IO_ChDir( IO_EditPathForDos(Path) ) ;
end ;
{ Make the directory for a presumably correct format Path and get any error }
Function IO_MkDir(Const Path : PathStr) : Integer ;
begin
{$I-} mkdir(Path) ;
{$I+} IO_MkDir := IOResult ;
end ;
{ Jeffrey Watson's original Pascal Edit path If statement has been
replaced with EditPatForDos, and the call to BP Mkdir with IO_MkDir }
{ Don't presume that Path is correctly formatted, edit it,
then make the directory, and get any error }
Function IO_MD(Const Path : PathStr) : Integer ;
begin
IO_MD := IO_MkDir(IO_EditPathForDos(Path) ) ;
end ;
{ Remove the directory for a presumably correct format Path and get any error }
Function IO_RmDir(Const Path : PathStr) : Integer;
Begin
{$I-} rmdir( Path ) ;
{$I+} IO_RmDir := IOResult ;
End;
{ Jeffrey Watson's original Pascal Edit path If statement has been
replaced with EditPatForDos, and the call to BP Rmdir with IO_RmDir }
{ Don't presume that Path is correctly formatted, edit it,
then remove the directory, and get any error }
Function IO_RD(Const Path : PathStr) : Integer;
Begin
IO_RD := IO_RmDir( IO_EditPathForDos(Path) ) ;
End;
{ Jeffrey Watson's original Pascal If statement for translating Drive have been
replaced with a case statement, but I've kept the return of a null string on error}
{ GD: returns the current directory for a drive,
specified one of a variety of formats }
Function IO_GD(Drive : char) : PathStr ;
Var DosDrive : byte ;
Path : PathStr ;
begin
Case Drive Of
'A'..'Z' : DosDrive := ord(Drive) - 64 ;
'a'..'z' : DosDrive := ord(Drive) - 96 ;
'0'..'9' : DosDrive := ord(Drive) - 48 ;
#00..#26 : DosDrive := ord(Drive) ;
#32 : DosDrive := 0 ;
End;
{ If one didn't check for a valid drive, GetDir would be perfectly content
to hand back a bogus path }
If DiskSize(DosDrive) <> -1 then { All Invalid Drives should return -1 }
Begin
Getdir(DosDrive, Path); { GetDir DOES NOT AFFECT IORESULT! }
IO_GD := Path;
End
else
IO_GD := '' ; { This makes it readily apparent that a problem occured }
end ; (* GD *)
{ Jeffrey Watson's original Pascal Function written before 6.0 using Intr has been
translated to BASM because doing so is always smaller, faster and SAFER than
using Intr.
Borland's approach, while a kludge, is one of very few ways of Intr getting
around Intel's insistance on hardcoded interrupt numbers. This makes MsDos
much worse because while making it share code with Intr may save space,
setting up separate code for putting Reg values into registers and doing a
Int 21 would have been much safer. }
{ Finding out if SHARE.EXE has been installed which would mean additional flags
must be set to get the proper filemode }
Function IO_ShareInstalled : Boolean ; assembler;
asm
mov dx, ds { save BP Data Segment }
xor bl, bl { setup for return }
mov AH, $30 { Using Int 21, Function 30H, Get Dos Version }
int 21h
cmp al, 3 { If not Dos 3+, jump to end, return False }
jb @1
xor ax, ax
mov AH, 10h { Using Int 2F, Function 10H, Get SHARE.EXE Installation Status }
int 2Fh
jc @1 { Carry flag set on error, jump to end, return False }
cmp Al, 0FFh { if interrupt returns $FF, SHARE.EXE is installed }
jne @1 { if not, return FALSE }
mov bl, 1 { else return TRUE }
@1: mov al, bl { put in actual return register }
mov ds, dx
end;
{ ReadOnly: returns readonly status flag }
Function IO_ReadOnly : Byte;
begin
if IO_ShareInstalled then
IO_ReadOnly := $20
else
IO_ReadOnly := $00 ;
end ; { ReadOnly }
(*.PA*)
{ ReadWrite: returns ReadWrite status flag }
Function IO_ReadWrite : Byte;
begin
if IO_ShareInstalled then
IO_ReadWrite := $42
else
IO_ReadWrite := $02 ;
end ; { ReadWrite }
{ WriteOnly: returns writeonly status flag }
Function IO_WriteOnly : Byte;
begin
if IO_ShareInstalled then
IO_WriteOnly := $31
else
IO_WriteOnly := $01 ;
end ; { WriteOnly }
{ Returns the proper Status flag, allows one to use the File mode constants as
mnumonics for setting the FileMode variable. I was using it in quite a few
Functions below until I found that though DOS may pay attention to the file
mode when opening a file, BP code expects Open Typed and UnTyped files to be
fmInOut, Text files being read to be fmInput, and Text file being written to
be fmOutput regardless of the state of the FileMode variable. }
Function IO_FileMode(InMode : Word) : Byte;
Begin
If InMode >= fmInput Then { If InMode = [fmInput, fmOutput, fmInOut], then reset }
Dec(InMode, fmInput);
If InMode > 2 Then
InMode := 2;
Case InMode Of
0 : InMode := IO_ReadOnly;
1 : InMode := IO_WriteOnly;
2 : InMode := IO_ReadWrite;
End;
IO_FileMode := InMode;
End;
{ The following Open.. Functions largely follow the form of Open Methods that are
used in objects written by Jeffrey Watson }
{ Reset the text file and get the error }
Function IO_ResetText(Var TextFile : Text) : Integer;
Begin
{$I-} Reset(TextFile);
{$I+} IO_ResetText := IOResult;
End;
{ Rewrite the text file and get the error }
Function IO_RewriteText(Var TextFile : Text) : Integer;
Begin
{$I-} Rewrite(TextFile);
{$I+} IO_RewriteText := IOResult;
End;
{ Append to the text file and get the error }
Function IO_AppendText(Var TextFile : Text) : Integer;
Begin
{$I-} Append(TextFile);
{$I+} IO_AppendText := IOResult;
End;
{ Open the text file called PathName according the OpenType and get the error }
Function IO_OpenText(Const PathName : PathStr;
Var TextFile : Text;
OpenType : OpenTextEnum) : Integer;
Begin
Assign(TextFile, PathName);
Case OpenType Of
ResetFile : IO_OpenText := IO_ResetText(TextFile); {Mode = fmInput}
RewriteFile : IO_OpenText := IO_RewriteText(TextFile); {Mode = fmOutput}
AppendFile : IO_OpenText := IO_AppendText(TextFile); {Mode = fmOutput}
End;
End;
{ Read a string from the text file and get the error }
Function IO_ReadTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
Begin
{$I-} Read(TextFile, TextStr); {$I+}
IO_ReadTextStr := IOResult;
End;
{ Read a line from the text file and get the error }
Function IO_ReadLnTextStr(Var TextFile : Text; Var TextStr : String) : Integer;
Begin
{$I-} Readln(TextFile, TextStr); {$I+}
IO_ReadLnTextStr := IOResult;
End;
{ Write a string to the text file and get the error }
Function IO_WriteTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
Begin
{$I-} Write(TextFile, TextStr); {$I+}
IO_WriteTextStr := IOResult;
End;
{ Write a line to the text file and get the error }
Function IO_WriteLnTextStr(Var TextFile : Text; Const TextStr : String) : Integer;
Begin
{$I-} Writeln(TextFile, TextStr); {$I+}
IO_WritelnTextStr := IOResult;
End;
{ Reset the untyped file and get the error }
Function IO_ResetFile(Var GenericFile : File) : Integer;
Begin
{$I-} Reset(GenericFile);
{$I+} IO_ResetFile := IOResult;
End;
{ Rewrite the untyped file and get the error }
Function IO_RewriteFile(Var GenericFile : File) : Integer;
Begin
{$I-} Rewrite(GenericFile);
{$I+} IO_RewriteFile := IOResult;
End;
{ Open the untyped file called PathName according the OpenType and get the error }
Function IO_OpenFile( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum) : Integer;
Begin
Assign(GenericFile, PathName);
Case OpenType Of
ResetFile : IO_OpenFile := IO_ResetFile(GenericFile); {Mode = fmInOut}
RewriteFile : IO_OpenFile := IO_RewriteFile(GenericFile); {Mode = fmInOut}
End;
End;
{ These next 3 Functions could be the only way of using this unit on typed files.
The compiler won't let you work with typed files directly through File, but if you
make BufSize = SizeOf(Record) then you can approximate the behavior. Personally,
though, I think any recurringly used typed file should have an associated object
written to read and write from/to it, and that object can deal with I/O errors. }
{ Reset the untyped file with a set buffer size and get the error }
Function IO_ResetFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
Begin
{$I-} Reset(GenericFile, BufSize);
{$I+} IO_ResetFileBlock := IOResult;
End;
{ Rewrite the untyped file with a set buffer size and get the error }
Function IO_RewriteFileBlock(Var GenericFile : File; BufSize : Word) : Integer;
Begin
{$I-} Rewrite(GenericFile, BufSize);
{$I+} IO_RewriteFileBlock := IOResult;
End;
{ Open the untyped file called PathName according the OpenType, change the file
buffer size to the given value, and get the error }
Function IO_OpenFileBlock( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum;
BufSize : Word) : Integer;
Begin
Assign(GenericFile, PathName);
{Mode = fmInOut}
Case OpenType Of
ResetFile : IO_OpenFileBlock := IO_ResetFileBlock(GenericFile, BufSize);
RewriteFile : IO_OpenFileBlock := IO_RewriteFileBlock(GenericFile, BufSize);
End;
End;
{ The ones to use with BlockRead/Write on generic files }
{ Reset the untyped file with a buffer = 1 and get the error }
Function IO_ResetFileBlock1(Var GenericFile : File) : Integer;
Begin
{$I-} Reset(GenericFile, 1);
{$I+} IO_ResetFileBlock1 := IOResult;
End;
{ Rewrite the untyped file with a buffer = 1 and get the error }
Function IO_RewriteFileBlock1(Var GenericFile : File) : Integer;
Begin
{$I-} Rewrite(GenericFile, 1);
{$I+} IO_RewriteFileBlock1 := IOResult;
End;
{ Open the untyped file called PathName according the OpenType, change the file
buffer size to 1, and get the error }
Function IO_OpenFileBlock1( Const PathName : PathStr;
Var GenericFile : File;
OpenType : OpenFileEnum) : Integer;
Begin
Assign(GenericFile, PathName);
{ Mode = fmInOut }
Case OpenType Of
ResetFile : IO_OpenFileBlock1 := IO_ResetFileBlock1(GenericFile);
RewriteFile : IO_OpenFileBlock1 := IO_RewriteFileBlock1(GenericFile);
End;
End;
{ Close the GenericFile and get the error }
Function IO_Close(Var GenericFile : File) : Integer;
Begin
{$I-} Close(GenericFile);
{$I+} IO_Close := IOResult;
End;
{ Close the TextFile and get the error }
Function IO_CloseText(Var TextFile : Text) : Integer;
Begin
{$I-} Close(TextFile);
{$I+} IO_CloseText := IOResult;
End;
{ Close the GenericFile and get the error, but ignore if it was already closed }
Function IO_CloseFile(Var GenericFile : File) : Integer;
Var ErrCode : Integer;
Begin
ErrCode := IO_Close(GenericFile);
If ErrCode = Ord(FileNotOpen) Then { Really, if it was already closed why bother about it }
IO_CloseFile := 0
Else
IO_CloseFile := ErrCode;
End;
{ Close the TextFile and get the error, but ignore if it was already closed }
Function IO_CloseTextFile(Var TextFile : Text) : Integer;
Var ErrCode : Integer;
Begin
ErrCode := IO_CloseText(TextFile);
If ErrCode = Ord(FileNotOpen) Then { Really, if it was already closed why bother about it }
IO_CloseTextFile := 0
Else
IO_CloseTextFile := ErrCode;
End;
{ The File is pointed to by FullName if you can open and close it
successfully. I recently found out that, when used to verify the
existance of an executable on a drive accessed through a new
NetWare VLM, this will frequently return False because the VLM
won't allow an executable to be opened as InOut as all non-text
files are in a BP program. }
Function IO_FileIsHere(FullName : PathStr) : Boolean;
Var ErrCode : Integer;
GenericFile : File;
Begin
ErrCode := IO_OpenFile(FullName, GenericFile, ResetFile);
If ErrCode = 0 Then
IO_FileIsHere := IO_Close(GenericFile) = 0
Else
IO_FileIsHere := False;
End;
{ This Function works for both files AND directories }
Function IO_Exists(Path : PathStr ; Attr : byte) : Boolean ;
Var DirInfo : Dos.SearchRec;
InDirStr : Dos.DirStr;
InNameStr : Dos.NameStr;
InExtStr : Dos.ExtStr;
begin
If Attr = Dos.Directory Then
Path := IO_AddSlash(Path);
FSplit(Path, InDirStr, InNameStr, InExtStr);
InDirStr := IO_AddSlash(InDirStr);
If InNameStr = '' Then
InNameStr := '*';
If InExtStr = '' Then
InExtStr := '.*';
findfirst(InDirStr + InNameStr + InExtStr, Attr, DirInfo) ;
IO_Exists := doserror = 0 ;
end ;
{ Jeffrey Watson's original Pascal Functions GetFileAttr, SetFileAttr,
GetFileTime, SetFileTime have each been broken into 2 parts: one that
does the setup and cleanup and calls the other which does everything else
in between. This way the in between part can be accessed by code that has
already setup the file for other uses. Also, the setup and cleanup code
is shared with other Functions in this unit. }
{ Get the file creation time for an OPEN!!! file and any error }
Function IO_GetFTime(Var GenericFile : File; Var InTime : Longint) : Integer;
Begin
{ Since DosError is not traditional associated with this error, I'm
forcing it to be sure that the BP Function is called with an open file }
If FileRec(GenericFile).Mode = fmClosed Then
IO_GetFTime := 103
Else
Begin
getftime(GenericFile, InTime) ;
IO_GetFTime := Dos.DosError;
End;
End;
{ Set the file creation time for an OPEN!!! file and get any error }
Function IO_SetFTime(Var GenericFile : File; InTime : Longint) : Integer;
Begin
{ Since DosError is not traditional associated with this error, I'm
forcing it to be sure that the BP Function is called with an open file }
If FileRec(GenericFile).Mode = fmClosed Then
IO_SetFTime := 103
Else
Begin
setftime(GenericFile, InTime) ;
IO_SetFTime := Dos.DosError;
End;
End;
{ Open the file, Get its creation time, close it, and get any error }
Function IO_GetFileTime(Const PathName : PathStr; Var Time : Longint) : Integer;
Var GenericFile : file ;
ErrCode : word ;
begin
ErrCode := 0;
ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
If ErrCode = 0 then
begin
ErrCode := IO_GetFTime(GenericFile, Time) ;
If ErrCode = 0 Then
ErrCode := IO_Close(GenericFile);
end ; (* if *)
IO_GetFileTime := ErrCode ;
end ;
{ Open the file, Set its creation time, close it, and get any error }
Function IO_SetFileTime(Const PathName : PathStr ; Time : Longint) : Integer;
Var GenericFile : file ;
ErrCode : Integer ;
begin
ErrCode := 0;
ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
If ErrCode = 0 then
begin
ErrCode := IO_SetFTime(GenericFile, Time) ;
If ErrCode = 0 Then
ErrCode := IO_Close(GenericFile);
end ; (* if *)
IO_SetFileTime := ErrCode ;
end ; (* SetFileTime *)
{ It was using getfattr that caused me to write this unit and call for better
generic I/O Functions, when I used GetFAttr but was looking for 0's in Attr
to flag an error instead of DosError. Well, a file that has been backed up
and not changed since will always return a 0. It is not nice to punish your
users for routinely backing up their hard disk. I won't have that problem
using either of the 2 following Functions }
{ Get the Attr for GenericFile, presuming that GenericFile is Assigned but Closed }
Function IO_GetFAttr(Var GenericFile : File; Var Attr : word) : Integer;
Var ErrCode : word ;
begin
getfattr(GenericFile,Attr) ;
if Dos.DosError <> 0 then
Attr := $FFFF ;
IO_GetFAttr := Dos.DosError;
end ;
{ Get the Attr for PathName }
Function IO_GetFileAttr(Const PathName : PathStr; Var Attr : word) : Integer;
Var GenericFile : file ;
begin
assign(GenericFile,PathName) ;
IO_GetFileAttr := IO_GetFAttr(GenericFile,Attr) ;
end ;
{ Set the Attr for GenericFile, presuming that GenericFile is Assigned but Closed }
Function IO_SetFAttr(Var GenericFile : File; Attr : word) : Integer;
Var ErrCode : word ;
begin
setfattr(GenericFile, Attr) ;
IO_SetFAttr := Dos.DosError;
end ; (* GetFileAttr *)
{ Set the Attr for PathName }
Function IO_SetFileAttr(Const PathName : PathStr ; Attr : word) : Integer ;
Var GenericFile : file ;
begin
assign(GenericFile,PathName) ;
IO_SetFileAttr := IO_SetFAttr(GenericFile,Attr) ;
end ; (* SetFileAttr *)
{ The following Rename, Erase, and Kill file Functions are adaptations of
code originally done by Jeffrey Watson }
{ Rename the GenericFile to the NewName, presuming that GenericFile
is Assigned but Closed }
Function IO_Rename(Var GenericFile : File; Const NewName : PathStr) : Integer;
Begin
{$I-} Rename(GenericFile, NewName) ;
{$I+} IO_Rename := ioresult ;
End;
{ Assign OldName to OldFile variable, Rename the file to NewName, and get any error }
Function IO_RenameFile( Const OldName : PathStr;
Const NewName : PathStr) : integer ;
Var Oldfile : file ;
begin
assign(Oldfile,OldName) ;
IO_RenameFile := IO_Rename(OldFile,NewName) ;
end ; { IO_RenameFile }
{ Erase GenericFile, presuming that GenericFile is Assigned but Closed }
Function IO_Erase(Var GenericFile : File) : Integer;
Begin
{$I-} erase(GenericFile) ;
{$I+} IO_Erase := ioresult ;
End;
{ Erase TextFile, presuming that TextFile is Assigned but Closed }
Function IO_EraseText(Var TextFile : Text) : Integer;
Begin
{$I-} erase(TextFile) ;
{$I+} IO_EraseText := ioresult ;
End;
{ Erase PathName and return any error }
Function IO_EraseFile(Const PathName : PathStr) : integer ;
Var GenericFile : file ;
begin
assign(GenericFile, PathName) ;
IO_EraseFile := IO_Erase(GenericFile) ;
end ;
{ Killing a File differs from erasing it it that we only want to make sure it
doesn't exists afterwards. It doesn't matter if it didn't exist before }
Function IO_KillAFile(Const PathName : PathStr) : integer ;
Var FileToKill : File;
ErrCode : Integer;
Begin
ErrCode := IO_OpenFile(PathName, FileToKill, ResetFile);
Case IO_GetErrorEnum(ErrCode) Of
NoError : Begin
ErrCode := IO_Close(FileToKill);
If ErrCode = 0 Then
ErrCode := IO_Erase(FileToKill);
End;
FileNotFound : ErrCode := 0; { In killing a file, we don't mind if it doesn't exist }
End;
IO_KillAFile := ErrCode;
End;
{ IO_EraseFiles returns 0 if the files matching Path & FileSpec are erased }
Function IO_EraseFiles(Path, FileSpec : PathStr) : integer;
var ErrCode : word ;
DirInfo : Dos.SearchRec;
begin
Path := IO_AddSlash(Path);
If FileSpec[1] = '\' Then Delete(FileSpec, 1, 1);
findfirst(Path+FileSpec,Dos.AnyFile,DirInfo) ;
ErrCode := 0 ;
while (ErrCode = 0) and (doserror = 0) do
begin
{ FindFirst gets directories as well so test Attr }
If (DirInfo.Attr <> Directory) Then
ErrCode := IO_EraseFile(Path + DirInfo.Name) ;
findnext(DirInfo) ;
End ; (* while-do *)
IO_EraseFiles := ErrCode ;
end ; (* IO_EraseFiles *)
{ This was the Function where the previous misuse of GetFAttr (See IO_GetFAttr above)
got me into trouble. It is not good if your program bombs with a runtime error every
time it tries to remove a directory containing unchanged, normal, backed up files.
At some point, I'll switch this to using a pseudo-stack or linked list on the heap,
to eliminate any possiblity of stack overflow. If anybody would like to do it for
me, feel free to send me the code. }
{$S+} { Stack heavy recursive Function using 388 bytes for each LEVEL of subdirectory }
Function IO_Remove(InPath : PathStr) : Integer;
{$S-}
Var FAttrib : Word;
ThisFile : File;
SrchRecd : SearchRec;
InCurrDir,
DefCurrDir : DirStr;
{ This cleaned up the code below nicely }
Function IOErrorOccured(ErrCode : Integer) : Boolean;
Begin
If ErrCode <> 0 Then
Begin
IO_Remove := ErrCode;
IOErrorOccured := True;
End
Else
IOErrorOccured := False;
End;
Begin
IO_Remove := 0;
InPath := IO_DelSlash(InPath); {We are interested in directories as files }
Assign(ThisFile, InPath);
If IOErrorOccured(IO_GetFAttr(ThisFile, FAttrib)) Then
Exit
Else
Begin
If (FAttrib AND VolumeID) = 0 Then { For Windows: faVolumeID }
Begin
If (FAttrib AND Directory) = 0 Then { For Windows: faDirectory }
Begin
{ If InPath points to an ordinary file make it eraseable and do so }
If IOErrorOccured(IO_SetFAttr(ThisFile, Archive)) Then
Exit
Else
If IOErrorOccured(IO_Erase(ThisFile)) Then
Exit;
End
Else
Begin
DefCurrDir := IO_GD(#0); { get current directory of default drive }
{ Check whether the removed directory is on the default drive }
If DefCurrDir[1] <> UpCase(InPath[1]) Then
Begin
{ If it is not, get the current directory of drive of
the removed directory }
InCurrDir := IO_GD(InPath[1]);
If InCurrDir = '' Then
Begin
{ If GD returns '', Function was given an invalid path }
IO_Remove := Ord(PathNotFound);
Exit;
End;
End
Else
InCurrDir := DefCurrDir; { InPath is on the default drive }
{ Change current directory to InPath }
If IOErrorOccured(IO_ChDir(InPath)) Then
Exit;
{ Start finding files in it }
FindFirst(InPath+'\*.*', Dos.AnyFile, SrchRecd);
While Dos.DosError = 0 Do
Begin
With SrchRecd do
Begin
If ((Attr AND Directory) = 0) { Everything except }
OR (Name[1] <> '.') Then { '.' directories }
Begin { will be removed }
{ If a subdirectory is found, take care of it recursively }
If IOErrorOccured(IO_Remove(InPath+'\'+Name)) Then
Exit;
End;
End;
FindNext(SrchRecd); { continue search for files and subdirectories }
End;
{ Change back to the previous current directory }
If IOErrorOccured(IO_ChDir(InCurrDir)) Then
Exit;
{ So that you can remove this directory }
If IOErrorOccured(IO_RmDir(InPath)) Then
Exit;
{ If InPath was not on the default drive, then change to the current
directory there so that Remove leaves the computer back at the very
same drive and directory it was at before it started }
If DefCurrDir[1] <> UpCase(InPath[1]) Then
If IOErrorOccured(IO_ChDir(DefCurrDir)) Then
Exit;
End;
End;
End;
End;
{ Flush the text file to the Dos Buffers }
Function IO_FlushToDos(Var TextFile : Text) : Integer;
Begin
{$I-} Flush(TextFile);
{$I+} IO_FlushToDos := IOResult;
End;
{ Flush the text file to the Dos Buffers and then call
IO_DosFlush to flush to the disk file }
Function IO_FlushToDisk(Var TextFile : Text) : Integer;
Var ErrCode : Integer;
Begin
ErrCode := IO_FlushToDos(TextFile);
{ Now use Neil Rubenking's Function to flush DOS buffers to disk }
If ErrCode = 0 Then
ErrCode := IO_DosFlush(TextFile);
IO_FlushToDisk := ErrCode;
End;
{ Get the file size for an OPEN!!! file and get any error }
Function IO_FileSize(Var GenericFile : File; Var Size : Longint) : Integer;
Begin
{$I-} Size := FileSize(GenericFile);
{$I+} IO_FileSize := IOResult;
End;
{ If you open the file with a certain Blocksize, FileSize will return size
as the number of those blocks in the file }
Function IO_GetNoOfRecords(Const PathName : PathStr; Var Size : Longint; RecordSize : Word) : integer ;
Var GenericFile : file ;
ErrCode : Integer ;
Begin
ErrCode := 0;
ErrCode := IO_OpenFileBlock(PathName, GenericFile, ResetFile, RecordSize);
If ErrCode = 0 then
Begin
ErrCode := IO_FileSize(GenericFile, Size) ;
If ErrCode = 0 Then
ErrCode := IO_Close(GenericFile);
End ; (* if *)
IO_GetNoOfRecords := ErrCode ;
End;
{ This was originally Jeffrey Watson's code. }
{ Getting FileSize through FindFirst was approximately 60% faster ( on a 50Mhz 486DX2,
with fast hard drive and Smartdrv loaded) than opening the file as a file of 1 Byte
records and then using IO_FileSize }
Function IO_GetFileSize(Const PathName : PathStr; Var FileSize : Longint) : integer ;
Var DirInfo : Dos.SearchRec;
Begin
findfirst(PathName, Dos.Anyfile, DirInfo) ;
If Dos.DosError = 0 Then
FileSize := DirInfo.Size ;
IO_GetFileSize := Dos.DosError;
End;
{ Get the file position for an OPEN!!! file and get any error. Unlike FileSize
it would be unlikely for any programmer to intentionally try to find the
FilePos on a closed file }
Function IO_FilePos(Var GenericFile : File; Var FPos : Longint) : Integer;
Begin
{$I-} FPos := FilePos(GenericFile);
{$I+} IO_FilePos := IOResult;
End;
{ Seek a file position for an OPEN!!! file and get any error.}
Function IO_FileSeek(Var GenericFile : File; FSeek : Longint) : Integer;
Begin
{$I-} Seek(GenericFile, FSeek);
{$I+} IO_FileSeek := IOResult;
End;
{ Open file PathName, and then seek a file position and get any error.}
Function IO_GoFileSeek( Const PathName : PathStr;
Var GenericFile : File;
FSeek : Longint) : integer ;
Var ErrCode : Integer ;
Begin
ErrCode := 0;
ErrCode := IO_OpenFile(PathName, GenericFile, ResetFile);
If ErrCode = 0 then
ErrCode := IO_FileSeek(GenericFile, FSeek) ;
IO_GoFileSeek := ErrCode ;
End;
{ Read a block of bytes from an OPEN!!! file and get any error.}
Function IO_BlockRead( Var GenericFile : File;
Var Buffer;
Count : Word;
Var BytesRead : Word ) : Integer;
Begin
{$I-} BlockRead(GenericFile, Buffer, Count, BytesRead);
{$I+} IO_BlockRead := IOResult;
End;
{ Open file PathName, create a buffer, read the file into the buffer, then
close the file. Will not work with Files of size > 64K-8 (largest buffer).
User of Function responsible for knowing structure of file to correctly
use bytes read into buffer. User also responsible for using a
FreeMem(BuffPtr, FSize) after use to remove buffer from Heap.}
Function IO_BlockReadIntoHeap( Const PathName : PathStr;
Var BuffPtr : Pointer;
Var FSize : Longint ) : Integer;
Var ErrCode : Integer;
BytesRead : Word;
GenericFile : File;
Begin
ErrCode := 0;
ErrCode := IO_OpenFileBlock1(PathName, GenericFile, ResetFile);
If ErrCode = 0 Then
Begin
ErrCode := IO_FileSize(GenericFile, FSize);
If ErrCode = 0 Then
Begin
BytesRead := MaxAvail;
If BytesRead > 65528 Then
BytesRead := 65528;
IF FSize > BytesRead Then
ErrCode := Ord(InsufficientMemory)
Else
Begin
GetMem(BuffPtr, FSize);
{ This really only works if LocHeapFunc is set to 1 }
If Not Assigned(BuffPtr) Then
ErrCode := Ord(InsufficientMemory)
Else
Begin
ErrCode := IO_BlockRead(GenericFile, BuffPtr, FSize, BytesRead);
If ErrCode = 0 Then
If BytesRead <> FSize Then
ErrCode := Ord(DiskReadError);
End;
End;
If ErrCode in [Ord(NoError), Ord(InsufficientMemory)] Then
ErrCode := IO_CloseFile(GenericFile);
End;
End;
IO_BlockReadIntoHeap := ErrCode;
End;
{ Write a block of bytes to an OPEN!!! file and get any error.}
Function IO_BlockWrite( Var GenericFile : File;
Var Buffer;
Count : Word;
Var Result : Word) : Integer;
Begin
{$I-} BlockWrite(GenericFile, Buffer, Count, Result);
{$I+} IO_BlockWrite := IOResult;
End;
{ Open file PathName, get pointer to a buffer, write the buffer to the file,
then close the file. Will not work with Buffers of size > 64K. }
Function IO_BlockWriteFromHeap(Const PathName : PathStr;
BuffPtr : Pointer;
FSize : Word ) : Integer;
Var ErrCode : Integer;
BytesWritten : Word;
GenericFile : File;
Begin
ErrCode := 0;
If Not Assigned(BuffPtr) Then
ErrCode := 204 { Invalid Pointer Operation }
Else
Begin
If FSize > 65535 Then
ErrCode := Ord(DiskWriteError)
Else
Begin
ErrCode := IO_OpenFileBlock1(PathName, GenericFile, RewriteFile);
If ErrCode = 0 Then
Begin
ErrCode := IO_BlockWrite(GenericFile, BuffPtr, FSize, BytesWritten);
If ErrCode = 0 Then
If BytesWritten <> FSize Then
ErrCode := Ord(DiskWriteError);
End;
End;
End;
If ErrCode = 0 Then
ErrCode := IO_CloseFile(GenericFile);
IO_BlockWriteFromHeap := ErrCode;
End;
{ EO Functions are Boolean because they are Boolean to start with and are
often embedded in While..Do and Repeat..Until loops. Since an error
forces them to return False, the ErrCode can be examined afterward to
determine whether it was a normal or abnormal End }
Function IO_EOF(Var GenericFile : File; Var ErrCode : Integer) : Boolean;
Begin
{$I-} IO_EOF := EOF(GenericFile);
{$I+} ErrCode := IOResult;
If ErrCode <> 0 Then
IO_EOF := False;
End;
Function IO_EOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Begin
{$I-} IO_EOLn := EOLn(TextFile);
{$I+} ErrCode := IOResult;
If ErrCode <> 0 Then
IO_EOLn := False;
End;
Function IO_SeekEOF(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Begin
{$I-} IO_SeekEOF := SeekEOF(TextFile);
{$I+} ErrCode := IOResult;
If ErrCode <> 0 Then
IO_SeekEOF := False;
End;
Function IO_SeekEOLn(Var TextFile : Text; Var ErrCode : Integer) : Boolean;
Begin
{$I-} IO_SeekEOLn := SeekEOLn(TextFile);
{$I+} ErrCode := IOResult;
If ErrCode <> 0 Then
IO_SeekEOLn := False;
End;
{ compares 2 equal size areas of memory byte for byte, will only be true if
they are IDENTICAL in the interval [Index..Index+Size]. Written originally
to compare 2 equal type records which the compiler should be able to do
since it can use REP MOVSB to copy a record to another of the same type.
Index = 0 will start at the first byte. In simular fashion to the FillChar
and Move Procedures, the User is responsible for making sure that Size
accounts for starting place Index }
{ Jeffrey Watson's original Pascal Function written before 6.0 using a while loop
and array indexing has been translated to BASM using a ASM string CMPSB because
doing so is much smaller and faster. }
Function IO_Equals(Var X, Y; Index : word; Size : word) : Boolean; assembler;
asm
mov dx, ds
lds si, X { Get X Address }
les di, Y { Get Y Address }
add si, word ptr Index
add di, word ptr Index { Move Index bytes over for start of compare }
mov cx, Size { Compare Size Bytes }
mov bl, 1 { Preset return for True }
cld { make compare go forward }
repe cmpsb { run through comparison }
je @1 { if Zero flag still zero, than successful completion }
mov bl, 0 { else we stopped when two bytes found different }
@1: mov al, bl { set the return }
mov ds, dx
end;
{ following are methods for objects to do some commonly done file processes }
{ This object will find files and then let the user deal with them it a
DoFileOperation method that overrides the abstract one here. Using Abstract
in the one here will force that. }
{ If not even one file matching Path can be found the object will fail }
Constructor TFindFileObj.InitAndFindFirst(Const Path : PathStr; Attr: Word);
Begin
FindFirst(Path, Attr, FSearch);
If DosError <> 0 Then
Fail
Else
Begin
FFError := DosError;
ParseFSearch;
End;
End;
Destructor TFindFileObj.EndFindFile;
Begin
End;
{ Parse and store everything about the file for easy access by methods }
Procedure TFindFileObj.ParseFSearch;
Var DT : DateTime;
PrdPos : word;
Begin
With FSearch Do
Begin
FAttr := Attr;
FTime := Time;
FSize := Size;
FNameExt := Name;
End;
UnpackTime(FTime, DT);
With DT do
Begin
FYear := Year;
FMonth := Month;
FDay := Day;
FHour := Hour;
FMin := Min;
FSec := Sec;
End;
PrdPos := Pos('.',FNameExt);
If PrdPos <= 0 then
PrdPos := Succ(Length(FNameExt));
FName := Copy(FNameExt, 1, Pred(PrdPos));
FExt := Copy(FNameExt, Succ(PrdPos), 3);
End;
{ Find the next file }
Procedure TFindFileObj.DoFindNext;
Begin
FindNext(FSearch);
ParseFSearch;
FFError := DosError;
End;
{ Here's the loop that one usually has to create out of whole cloth each time
files have to be found. Using FileOpError allows the user to pass any errors
up to the program from the DoFileOperation method }
Function TFindFileObj.DoFindFileLoop : Integer;
Var FileOpError : Integer;
Begin
FileOpError := 0;
While (FFError = 0) and (FileOpError = 0) do
Begin
FileOpError := DoFileOperation;
DoFindNext;
End;
DoFindFileLoop := FileOpError;
End;
{ OVERRIDE this method }
Function TFindFileObj.DoFileOperation : Integer;
Begin
DoFileOperation := 0;
Abstract;
End;
{ TCopyFileObj copies Source to Dest a CopyBuffer at a time,
using BlockRead and BlockWrite }
Constructor TCopyFileObj.InitCopy(Const Source : PathStr; Const Dest : PathStr);
Var TempDir : DirStr;
TempName : NameStr;
TempExt : ExtStr;
Begin
CopyBuffer := Nil;
BuffSize := MaxAvail;
If BuffSize > 65528 Then
BuffSize := 65528;
GetMem(CopyBuffer, BuffSize);
{ This next would only work if LocHeapFunc = 1. And CopyBuffer shouldn't
be unassigned from lack of memory, but it may be so for other reasons}
If Not Assigned(CopyBuffer) Then
Fail;
{ Paths are stored full and in parts so that a descendent object below
that copies sets of files in a loop can replace them }
FSplit(Source, TempDir, TempName, TempExt);
SourceFullPath := Source;
SourcePath := TempDir;
SourceName := TempName + TempExt;
FSplit(Dest, TempDir, TempName, TempExt);
DestFullPath := Dest;
DestPath := TempDir;
DestName := TempName + TempExt;
End;
{ If CopyBuffer was successfully created, get rid of it here }
Destructor TCopyFileObj.EndCopy;
Begin
If Assigned(CopyBuffer) Then
FreeMem(CopyBuffer, BuffSize);
End ;
{ Set the Source and Dest to the same name }
Procedure TCopyFileObj.SetNames(Const FileName : Strg12) ;
Begin
SourceName := FileName;
DestName := FileName;
End;
{ Set the paths }
Procedure TCopyFileObj.SetPaths(Const SPath : PathStr; Const DPath : PathStr);
Begin
SourcePath := SPath;
DestPath := DPath;
End;
Function TCopyFileObj.GetErr : Integer;
Begin
GetErr := Err;
End;
{ Open the files with Block Size of 1 Byte }
Function TCopyFileObj.OpenFiles : Integer;
Begin
Err := IO_OpenFileBlock1( SourcePath+SourceName, SourceFile, ResetFile);
If Err = 0 Then
Err := IO_OpenFileBlock1( DestPath+DestName, DestFile, RewriteFile);
OpenFiles := Err;
End;
Function TCopyFileObj.CloseFiles : Integer;
Begin
Err := IO_CloseFile(SourceFile);
If Err = 0 Then
Err := IO_CloseFile(DestFile);
CloseFiles := Err;
End;
{ Copy from Source to Dest, a CopyBuffer at a time }
Function TCopyFileObj.DoFileCopy : Integer;
Var BytesRead, BytesWritten : Word;
Begin
If OpenFiles = 0 Then
Begin
repeat
Err := IO_Blockread(SourceFile, CopyBuffer^, BuffSize, BytesRead) ;
if Err = 0 then
Err := IO_BlockWrite(DestFile, CopyBuffer^, BytesRead, BytesWritten) ;
until (Err <> 0) or IO_Eof(SourceFile, Err)
or (BytesRead <> BuffSize) or (BytesRead <> BytesWritten);
If Err = 0 Then
Err := CloseFiles;
End;
DoFileCopy := Err ;
end ;
Function TCopyFileObj.DoCopies : Integer;
Var SrchRecd : Dos.SearchRec;
Begin
FindFirst(SourceFullPath, Dos.AnyFile, SrchRecd);
While (DosError = 0) and (Err = 0) do
Begin
If SrchRecd.Attr <> dos.directory Then
Begin
SetNames(SrchRecd.Name);
Err := DoFileCopy;
End;
If Err = 0 Then
FindNext(SrchRecd);
End;
DoCopies := Err;
End;
{ Move is identical to Copy except that Source is erased afterwards }
Constructor TMoveFileObj.InitMove(Source, Dest : PathStr);
Begin
Inherited InitCopy(Source, Dest);
End;
Destructor TMoveFileObj.EndMove;
Begin
Inherited EndCopy;
End;
Function TMoveFileObj.DoFileCopy : Integer;
Begin
Err := Inherited DoFileCopy;
If Err = 0 Then
Err := IO_EraseFile(SourcePath+SourceName);
End;
End.